/*──────────────────────────────────────────────────────────────────────────────┐
│Copyright 2024 Zhengyang Zhong (zhengyangz99@163.com)                          │
│                                                                               │
│Permission is hereby granted, free of charge, to any person obtaining a copy   │
│of this software and associated documentation files (the "Software"), to deal  │
│in the Software without restriction, including without limitation the rights   │
│to use, copy, modify, merge, publish, distribute, sublicense, and/or sell      │
│copies of the Software, and to permit persons to whom the Software is furnished│
│to do so, subject to the following conditions:                                 │
│                                                                               │
│The above copyright notice and this permission notice shall be included in all │
│copies or substantial portions of the Software.                                │
│                                                                               │
│THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR     │
│IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,       │
│FINIESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE    │
│AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER.        │
│LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,  │
│OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE  │
│SOFTWARE.                                                                      │
└──────────────────────────────────────────────────────────────────────────────*/
#include "../include/Q3DLineGraphWidget.h"
#include "../include/shaderSourceCode.h"

/*
  Outline
  - inherit class
  - manage events
  - pre-process data 
  - setup parameters
  - draw elements and texts
*/

/*
+----------------------------+
|                            |
|       Inherit class        |
|                            |
+----------------------------+
*/

Q3DLineGraphWidget::Q3DLineGraphWidget(QWidget *parent, int h, int w)
    : QOpenGLWidget(parent), function(new QOpenGLFunctions())
{
	maxHeight = h;
	maxWidth = w;
}

Q3DLineGraphWidget::~Q3DLineGraphWidget() {
	cleanup();
}
void Q3DLineGraphWidget::cleanup() 
{
	if (elementRenderProgram == nullptr)
		return;
	makeCurrent();
	elementVAO.destroy();
	posVBO.destroy();
	colorVBO.destroy();
	textVAO.destroy();
	textArrayVBO.destroy();
	textUVVBO.destroy();
	doneCurrent();
}

/* OpenGLWidget basic setting */
void Q3DLineGraphWidget::initializeGL() {
	initializeOpenGLFunctions();

	QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
	f->glClearColor(0, 0, 0, 1);
	f->glEnable(GL_DEPTH_TEST);
	f->glEnable(GL_CULL_FACE);

	initializeElementShaderProgram();
	initializeTextShaderProgram();
	
	timer.start(1, this);
}

void Q3DLineGraphWidget::initializeElementShaderProgram() 
{
	// elements include the point, line, and various shape, except for text
	elementRenderProgram = new QOpenGLShaderProgram;
	if(!elementRenderProgram->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource)) {
		qDebug() << "load vertex shader source failed!";
		close();
	}
	if(!elementRenderProgram->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource)) {
		qDebug() << "load fragment shader source failed!";
		close();
	}
	if(!elementRenderProgram->link()) {
		qDebug() << "link shader failed!";
		close();
	}
	if (!elementVAO.create())
		qDebug() << "create elementVAO failed";
	if (!posVBO.create())
		qDebug() << "failed create position vbo";
	if (!colorVBO.create())
		qDebug() << "failed create position vbo";
}

void Q3DLineGraphWidget::initializeTextShaderProgram()
{
	// The program to draw text
	textRenderProgram = new QOpenGLShaderProgram;
	if(!textRenderProgram->addShaderFromSourceCode(QOpenGLShader::Vertex, textVertexShaderSource)) {
		qDebug() << "load text vertex shader source failed";
		close();
	}
	if (!textRenderProgram->addShaderFromSourceCode(QOpenGLShader::Fragment, textFragShaderSource)) {
		qDebug() << "load text fragment shader source failed";
		close();
	}
	if (!textRenderProgram->link()) {
		qDebug() << "link text shader failed";
		close();
	}
	if (!textVAO.create())
		qDebug() << "create text VAO failed";
	if (!textArrayVBO.create()) {
		qDebug() << "create text array VBO failed";
		close();
	}
	if (!textUVVBO.create()) {
		qDebug() << "create text uv VBO failed";
		close();
	}
}

void Q3DLineGraphWidget::resizeGL(int width, int height) {
	height = height ? height : 1;
	glViewport(0, 0, width, height);
}

void Q3DLineGraphWidget::paintGL() {
	QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
	
	f->glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
	f->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	f->glEnable(GL_DEPTH_TEST);

	modelMatrix.setToIdentity();
	projection.setToIdentity();
	projection.perspective(45.0f, 1.0f, 0.1f, 100.0f);
	viewMatrix.setToIdentity();
	viewMatrix.lookAt(
		QVector3D(1, 1, 0.5),
		QVector3D(0, 0, 0),
		QVector3D(0, 1, 0));
	viewMatrix.translate(
		axisOfRotation.x(),
		axisOfRotation.y(),
		axisOfRotation.z());
	viewMatrix.rotate(rotation);
	viewMatrix.translate(
		-axisOfRotation.x(),
		-axisOfRotation.y(),
		-axisOfRotation.z());

	// build element shader to show points and lines
	elementRenderProgram->bind();
	elementRenderProgram->setUniformValue("projection", projection);
	elementRenderProgram->setUniformValue("viewMatrix", viewMatrix);
	elementRenderProgram->setUniformValue("modelMatrix", modelMatrix);
	showAxes();
	showZTicks();
	showMeshGrid(tickNum, tickNum);
	showTrajectories();
	elementRenderProgram->release();

	// build text shader to show labels and numbers on axes
	textRenderProgram->bind();
	textRenderProgram->setUniformValue("projection", projection);
	textRenderProgram->setUniformValue("viewMatrix", viewMatrix);
	textRenderProgram->setUniformValue("modelMatrix", modelMatrix);
	showXLabel("width");
	showYLabel("height");
	showZLabel("frame");
	showZTickLabels();
	textRenderProgram->release();
}

/*
+----------------------------+
|                            |
|       Manage events        |
|                            |
+----------------------------+
*/

/* Setup OpenGLWidget mouse events */
void Q3DLineGraphWidget::mousePressEvent(QMouseEvent *e)
{
	mousePressPosition = QVector2D(e->position());
}

void Q3DLineGraphWidget::mouseReleaseEvent(QMouseEvent *e)
{
	QVector2D diff = QVector2D(e->position()) - mousePressPosition;
	QVector3D n = QVector3D(diff.y(), diff.x(), 0.0).normalized();
	qreal acc = diff.length() / 500.0;
	rotationAxis = (rotationAxis * angularSpeed + n * acc ).normalized();
	angularSpeed += acc;
}

void Q3DLineGraphWidget::timerEvent(QTimerEvent *e)
{
	angularSpeed *= 0.97;
	if (angularSpeed < 0.01)
		angularSpeed = 0.0;
	else {
		rotation = QQuaternion::fromAxisAndAngle(rotationAxis, angularSpeed) * rotation;
		update();
	}
}

/*
+----------------------------+
|                            |
|      Pre-process data      |
|                            |
+----------------------------+
*/

void Q3DLineGraphWidget::setData(const std::vector<trackingData>& data) 
{
	tds = data;
	rescalePosition();
	update();
}

/* Rescale the data to interval [0, axisLength] */
void Q3DLineGraphWidget::rescalePosition()
{
	int objectNum = tds.size();
	for (int i = 0; i < objectNum; i++) {
		int frameNum = tds[i].positions.size();
		if (frameNum > zMax)
			zMax = frameNum;
		for (int j = 0; j < frameNum; j++) {
			float x = tds[i].positions[j].x();
			float y = tds[i].positions[j].y();
			tds[i].positions[j].setX((x / maxWidth)  * axisLength);
			tds[i].positions[j].setY((y / maxHeight) * axisLength);
		}
	}
	for (int i = 0; i < objectNum; i++) {
		int frameNum = tds[i].positions.size();
		for (int j = 0; j < frameNum; j++) {
			float z = tds[i].positions[j].z();
			tds[i].positions[j].setZ((z / zMax) * axisLength);
		}
	}
}

/*
+----------------------------+
|                            |
|      Setup parameters      |
|                            |
+----------------------------+
*/

void Q3DLineGraphWidget::setPointSize(float size)
{
	pointSize = size;
}

void Q3DLineGraphWidget::setAxisLabelSize(float textHeight)
{
	axisLabelSize = textHeight;
}

void Q3DLineGraphWidget::setTickLabelSize(float textHeight)
{
	tickLabelSize = textHeight;
}

void Q3DLineGraphWidget::setGeneralLabelSize(float textHeight)
{
	axisLabelSize = textHeight;
	tickLabelSize = textHeight;
}

void Q3DLineGraphWidget::setFontSize(int size)
{
	fontSize = size;
}

void Q3DLineGraphWidget::setFontType(QString name)
{
	fontName = name;
}

void Q3DLineGraphWidget::setFontColor(QColor color)
{
	fontColor = color;
}

void Q3DLineGraphWidget::setTextPadding(int padding) 
{
	textPadding = padding;
}

void Q3DLineGraphWidget::setTickNum(int num)
{
	tickNum = num;
}

void Q3DLineGraphWidget::setTickColor(QVector3D color) 
{
	tickColor = color;
}

void Q3DLineGraphWidget::setAxisColor(QVector3D color)
{
	axisColor = color;
}

void Q3DLineGraphWidget::setTrajectoryColor(QVector3D color)
{
	trajectoryColor = color;
}

void Q3DLineGraphWidget::setHighlightColor(QVector3D color)
{
	highlightColor = color;
}

void Q3DLineGraphWidget::setObjectPointColor(QVector3D color)
{
	objectPointColor = color;
}

void Q3DLineGraphWidget::setRandomColor()
{
	presentColorMode = RANDOM;
}

void Q3DLineGraphWidget::setEventColor(QVector3D beforeEvent, QVector3D afterEvent)
{
	presentColorMode = BYEVENT;
	beforeEventColor = beforeEvent;
	afterEventColor = afterEvent;
}

/*
+----------------------------+
|                            |
|   Draw elements and texts  |
|                            |
+----------------------------+
*/

// Draw elements: points, lines, arrows
void Q3DLineGraphWidget::passElementDataToVAO(const float *vertices, const float *colors, int verticesNum)
{
	elementVAO.bind();
	posVBO.bind();
	posVBO.setUsagePattern(QOpenGLBuffer::StaticDraw);
	posVBO.allocate(vertices, verticesNum * sizeof(float) * 3);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
	glEnableVertexAttribArray(0);
	posVBO.release();

	colorVBO.bind();
	colorVBO.setUsagePattern(QOpenGLBuffer::StaticDraw);
	colorVBO.allocate(colors, verticesNum * sizeof(float) * 3);
	glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
	glEnableVertexAttribArray(1);
	colorVBO.release();

	elementVAO.release();
}

void Q3DLineGraphWidget::showAxes()
{
	// Draw axes
	const float axisVertices[6 * 3] = {
		0.0f, 0.0f, 0.0f, 
		axisLength, 0.0f, 0.0f, // x axis 
		0.0f, 0.0f, 0.0f, 
		0.0f, axisLength, 0.0f, // y axis
		0.0f, 0.0f, 0.0f, 
		0.0f, 0.0f, axisLength  // z axis
	};
	float axisColors[6 * 3];
	for (int i = 0; i < 6; i++) {
		axisColors[i * 3]     = axisColor[0];
		axisColors[i * 3 + 1] = axisColor[1];
		axisColors[i * 3 + 2] = axisColor[2];
	}
	passElementDataToVAO(axisVertices, axisColors, 6);
	elementVAO.bind();
	glDrawArrays(GL_LINES, 0, 6);
	elementVAO.release();

	showArrows();
	showOrigin();
}

void Q3DLineGraphWidget::showArrows()
{
	float arrowVertices[6 * 3] = {
		0.0f,        axisLength,         0.0f,
		arrowRadius, axisLength - 0.01f, arrowRadius,
		-arrowRadius,axisLength - 0.01f, -arrowRadius,

		0.0f,         axisLength,         0.0f,
		-arrowRadius, axisLength - 0.01f, arrowRadius,
		arrowRadius,  axisLength - 0.01f, -arrowRadius
	};
	float arrowColors[6 * 3];
	for (int i = 0; i < 6; i++) {
		arrowColors[i * 3] = axisColor[0];
		arrowColors[i * 3 + 1] = axisColor[1];
		arrowColors[i * 3 + 2] = axisColor[2];
	}

	passElementDataToVAO(arrowVertices, arrowColors, 6);
	elementVAO.bind();
	glDrawArrays(GL_TRIANGLES, 0, 6);
	elementVAO.release();
}

void Q3DLineGraphWidget::showOrigin()
{
	const float originVertex[3] = {
		0.0f, 0.0f, 0.0f
	};
	const float originColor[3] = {
		axisColor[0], axisColor[1], axisColor[2]
	};
	passElementDataToVAO(originVertex, originColor, 1);
	elementVAO.bind();
	glPointSize(1.0f);
	glDrawArrays(GL_POINTS, 0, 1);
	elementVAO.release();
}

void Q3DLineGraphWidget::showZTicks()
{
	if (zMax == 0)
		return;
	int verticesLength = 0;
	std::vector<float> tickVertices;
	float tickInterval;
	if (zMax < tickNum) {
		verticesLength = zMax;
		tickVertices.resize(zMax * 2 * 3);
		tickInterval = axisLength / (zMax + 1);
		for (int i = 0; i < zMax; i++) {
			tickVertices[2 * 3 * i + 0] = 0.0f;
			tickVertices[2 * 3 * i + 1] = tickInterval * (i + 1);
			tickVertices[2 * 3 * i + 2] = 0.0f;
			tickVertices[2 * 3 * i + 3] = 0.01f;
			tickVertices[2 * 3 * i + 4] = tickInterval * (i + 1);
			tickVertices[2 * 3 * i + 5] = 0.01f;
		}
	} else {
		verticesLength = tickNum;
		tickVertices.resize(tickNum * 2 * 3);
		tickInterval = axisLength / (tickNum + 1);
		for (int i = 0; i < tickNum; i++) {
			tickVertices[2 * 3 * i + 0] = 0.0f;
			tickVertices[2 * 3 * i + 1] = tickInterval * (i + 1);
			tickVertices[2 * 3 * i + 2] = 0.0f;
			tickVertices[2 * 3 * i + 3] = 0.01f;
			tickVertices[2 * 3 * i + 4] = tickInterval * (i + 1);
			tickVertices[2 * 3 * i + 5] = 0.01f;
		}
	}
	std::vector<float> tickColors;
	tickColors.resize(verticesLength * 2 * 3);
	for (int i = 0; i < verticesLength * 2; i++) {
		tickColors[i * 3]     = tickColor[0];
		tickColors[i * 3 + 1] = tickColor[1];
		tickColors[i * 3 + 2] = tickColor[2];
	}
	passElementDataToVAO(tickVertices.data(), tickColors.data(), verticesLength * 2);
	elementVAO.bind();
	glDrawArrays(GL_LINES, 0, verticesLength * 2);
	elementVAO.release();
}

void Q3DLineGraphWidget::showMeshGrid(int xMeshNum, int yMeshNum)
{
	showXMeshLine(xMeshNum);
	showYMeshLine(yMeshNum);
}

void Q3DLineGraphWidget::showXMeshLine(int xMeshNum)
{
	float tickInterval = axisLength / (xMeshNum + 1);
	std::vector<float> tickVertices;
	tickVertices.resize(xMeshNum * 2 * 3);
	for (int i = 0; i < xMeshNum; i++) {
		tickVertices[2 * 3 * i]     = 0.0f;
		tickVertices[2 * 3 * i + 1] = 0.0f;
		tickVertices[2 * 3 * i + 2] = tickInterval * (i + 1);
		tickVertices[2 * 3 * i + 3] = axisLength;
		tickVertices[2 * 3 * i + 4] = 0.0f;
		tickVertices[2 * 3 * i + 5] = tickInterval * (i + 1);
	}
	std::vector<float> tickColors;
	tickColors.resize(xMeshNum * 2 * 3);
	for (int i = 0; i < xMeshNum * 2; i++) {
		tickColors[i * 3]     = tickColor[0];
		tickColors[i * 3 + 1] = tickColor[1];
		tickColors[i * 3 + 2] = tickColor[2];
	}
	passElementDataToVAO(tickVertices.data(), tickColors.data(), xMeshNum * 2);
	elementVAO.bind();
	glDrawArrays(GL_LINES, 0, xMeshNum * 2);
	elementVAO.release();
}

void Q3DLineGraphWidget::showYMeshLine(int yMeshNum)
{
	float tickInterval = axisLength / (yMeshNum + 1);
	std::vector<float> tickVertices;
	tickVertices.resize(yMeshNum * 2 * 3);
	for (int i = 0; i < yMeshNum; i++) {
		tickVertices[2 * 3 * i]     = tickInterval * (i + 1);
		tickVertices[2 * 3 * i + 1] = 0.0f;
		tickVertices[2 * 3 * i + 2] = 0.0f;
		tickVertices[2 * 3 * i + 3] = tickInterval * (i + 1);
		tickVertices[2 * 3 * i + 4] = 0.0f;
		tickVertices[2 * 3 * i + 5] = axisLength;
	}
	std::vector<float> tickColors;
	tickColors.resize(yMeshNum * 2 * 3);
	for (int i = 0; i < yMeshNum * 2; i++) {
		tickColors[i * 3]     = tickColor[0];
		tickColors[i * 3 + 1] = tickColor[1];
		tickColors[i * 3 + 2] = tickColor[2];
	}
	passElementDataToVAO(tickVertices.data(), tickColors.data(), yMeshNum * 2);
	elementVAO.bind();
	glDrawArrays(GL_LINES, 0, yMeshNum * 2);
	elementVAO.release();
}

void Q3DLineGraphWidget::showTrajectories()
{
	int objNum = tds.size();
	// for each object
	for (int i = 0; i < objNum; i++) {		
		std::vector<QVector3D> positions = tds[i].positions;
		std::vector<QVector3D> colors = tds[i].colors;
		int frameNum = positions.size();
		std::vector<float> objPos(frameNum * 3, 0.0f);
		std::vector<float> objTrajColors(frameNum * 3, 0.0f);
		std::vector<float> objPointColors(frameNum * 3, 0.0f);
		for (int j = 0; j < frameNum; j++) {
			objPos[j * 3]     = positions[j].x();
			objPos[j * 3 + 1] = positions[j].z();
			objPos[j * 3 + 2] = positions[j].y();

			objTrajColors[j * 3]     = colors[j].x();
			objTrajColors[j * 3 + 1] = colors[j].y();
			objTrajColors[j * 3 + 2] = colors[j].z();

			objPointColors[j * 3]    = colors[j].x();
			objPointColors[j * 3 + 1]= colors[j].y();
			objPointColors[j * 3 + 2]= colors[j].z();
		}
		
		passElementDataToVAO(objPos.data(), objTrajColors.data(), frameNum);
		elementVAO.bind();
		glDrawArrays(GL_LINE_STRIP, 0, frameNum);
		elementVAO.release();

		passElementDataToVAO(objPos.data(), objPointColors.data(), frameNum);
		elementVAO.bind();
		glPointSize(pointSize);
		glDrawArrays(GL_POINTS, 0, frameNum);
		elementVAO.release();
	}
}

void Q3DLineGraphWidget::highlightData(int hlId)
{
	if (hlId >= tds.size())
		return;
	int frameNum = tds[hlId].positions.size();
	for (int j = 0; j < frameNum; j++) {
		tds[hlId].colors[j] = highlightColor;
	}
	update();
}


// Draw labels and numbers
void Q3DLineGraphWidget::passTextDataToVAO(GLfloat labelVertices[])
{

	textVAO.bind();
	textArrayVBO.bind();
	textArrayVBO.setUsagePattern(QOpenGLBuffer::StaticDraw);
	textArrayVBO.allocate(labelVertices, 4 * sizeof(float) * 3);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
	glEnableVertexAttribArray(0);
	textArrayVBO.release();
	
	textUVVBO.bind();
	textUVVBO.setUsagePattern(QOpenGLBuffer::StaticDraw);
	textUVVBO.allocate(labelUVs, 4 * sizeof(float) * 2);
	glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0);
	glEnableVertexAttribArray(1);
	textUVVBO.release();

	glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
	textVAO.release();
}

void Q3DLineGraphWidget::createTextTexture(QString label)
{
	/* render text in 3D space */
	// setup the text font and the size
	QFont font(fontName, fontSize);
	QFontMetrics metrics(font);
	int textWidth = metrics.boundingRect(0, 0, 1000, 1000,
										 Qt::TextWordWrap, label).width();
	int textHeight = metrics.height();
	// QSize metricSize = metrics.size(Qt::TextSingleLine, label);
	textureWidth  = textWidth  + 2 * textPadding;
	textureHeight = textHeight + 2 * textPadding;

	// draw text on an image
	QImage imgFrame(textureWidth, textureHeight, QImage::Format_RGBA8888);
	imgFrame.fill(Qt::transparent);
	QPainter painter(&imgFrame);
	painter.setFont(font);
	painter.setPen(fontColor);
	painter.drawText(0, 0, imgFrame.width(), imgFrame.height(),
					 Qt::AlignCenter, label);
	painter.end();

	// pass the image to a texture object
	delete texture;
	texture = new QOpenGLTexture(imgFrame.mirrored());
	texture->setMinMagFilters(QOpenGLTexture::Linear, QOpenGLTexture::Linear);
}

void Q3DLineGraphWidget::showZTickLabels()
{
	if (zMax == 0)
		return;

	float zloc = 0.0f;
	QString zTickLabel;
	float rescale_h = tickLabelSize ;
	float rescale_w = tickLabelSize / textureHeight * textureWidth;
	int  count = zMax < tickNum ? zMax : tickNum;
	/*
	  if zmax < tickNum;
	  for i in zmax: print i

	  else
	  for i in tickNum: print int(zmax / (tickNum + 1) * i)

	 */
	for (int i = 0; i < count; i++) {
		zloc = axisLength / float(count) * i;
		zTickLabel = QString::number(zMax < tickNum ? i : int(zMax / (tickNum + 1) * i));
		createTextTexture(zTickLabel);
		texture->bind();
		GLfloat zTickLabelVertices[] = {
			0.0f, zloc,        0.0f,// 左上角
			0.0f, zloc - rescale_h, 0.0f,// 左下角
			rescale_w, zloc,        -rescale_w,// 右上角
			rescale_w, zloc - rescale_h, -rescale_w // 右下角
		};
		passTextDataToVAO(zTickLabelVertices);
	}
}

void Q3DLineGraphWidget::showXLabel(std::string label)
{
	QString text = QString::fromStdString(label);
	createTextTexture(text);

	float rescale_w = axisLabelSize / textureHeight * textureWidth;
	float proj_h    = axisLabelSize * 0.707f;
	rescale_w = rescale_w > axisLength ? axisLength : rescale_w;
	proj_h    = proj_h    > axisLength ? axisLength : proj_h;

	texture->bind();
	GLfloat xLabelVertices[] = {
		/*
		|- top-left corner xzy location 
		|_ buttom-left corner xzy location
		   top-right corner xzy location    -|
		   buttom-right corner xzy location _|
		*/
		-proj_h,  proj_h + 0.01f, axisLength,
		0.0f,     0.01f,          axisLength,
		-proj_h,  proj_h + 0.01f, axisLength - rescale_w,
		0.0f,     0.01f,          axisLength - rescale_w 
	};
	passTextDataToVAO(xLabelVertices);
}

void Q3DLineGraphWidget::showYLabel(std::string label)
{
	QString text = QString::fromStdString(label);
	createTextTexture(text);

	float rescale_w = axisLabelSize / textureWidth * textureHeight;
	float proj_h    = axisLabelSize * 0.707f;
	rescale_w = rescale_w > axisLength ? axisLength : rescale_w;
	proj_h    = proj_h    > axisLength ? axisLength : proj_h;

	texture->bind();
	GLfloat yLabelVertices[] = {
		/*
		|- top-left corner xzy location 
		|_ buttom-left corner xzy location
		   top-right corner xzy location    -|
		   buttom-right corner xzy location _|
		*/
		axisLength - rescale_w, 0.01f + proj_h, -proj_h,
		axisLength - rescale_w, 0.01f,         0.0f,
		axisLength,             0.01f + proj_h, -proj_h,
		axisLength,             0.01f,         0.0f
	};
	passTextDataToVAO(yLabelVertices);
}

void Q3DLineGraphWidget::showZLabel(std::string label)
{
	QString text = QString::fromStdString(label);
	createTextTexture(text);

	float rescale_h = axisLabelSize;
	float rescale_w = axisLabelSize / textureWidth * textureHeight;
	float proj_w    = rescale_w * 0.707f;
	rescale_h = rescale_h > axisLength ? axisLength : rescale_h;
	proj_w    = proj_w    > axisLength ? axisLength : proj_w;

	texture->bind();
	GLfloat zLabelVertices[] = {
		/*
		|- top-left corner xzy location 
		|_ buttom-left corner xzy location
		   top-right corner xzy location    -|
		   buttom-right corner xzy location _|
		*/
		0.0f,   axisLength,             0.0f,
		0.0f,   axisLength - rescale_h, 0.0f,
		proj_w, axisLength,             -proj_w,
		proj_w, axisLength - rescale_h, -proj_w 
	};
	passTextDataToVAO(zLabelVertices);
}
void Q3DLineGraphWidget::checkError() 
{
	errorCode = glGetError();
	if (errorCode != GL_NO_ERROR)
		qDebug() << "error code = " << errorCode;
}

